<?php
/**
 * Data Table 
 * Componente de renderização de DataGrids
 * @author benrainir
 */
require_once 'DataTableColumn.php';	
class Zend_View_Helper_DataTable extends Zend_View_Helper_Abstract
{
    /**
     * Model
     * @var object
     */
    private $_model;
            
    /**
     * Html
     * @access private
     * @var string
     */
    private $html;
    
    /**
     * Columns 
     * @var array
     */
    private $columns = array();    
    
    /**
     * Data
     * @var array
     */
    private $data = array();

    /**
     * Data Filters
     * Criterio de filtragem (pesquisa) de cada campo
     * @var array
     */
    private $dataFilters = array();
    
    private $order;
    
    protected $_options = array();
    
    /**
     * Construtor
     * @param $model
     * @param array $options
     */
    public function dataTable($model, $options = array())
    {
    	$this->_model = $model;
    	$this->_options = $options;
    	//FIXME: Paginação + Busca (Resetar Página)
    	
    	$editColumnAction = (isset($options['editColumn'])) ? $options['editColumn'] : true;
        $editHeader = (isset($options['editHeader'])) ? $options['editHeader'] : 'Editar';
        $classColumnAction = (isset($options['editClass'])) ? $options['editClass'] : 'editar';
        $atributoHtml = (isset($options['htmlInputColumn'])) ? $options['htmlInputColumn'] : '';
        
        $columns = ($this->_getOption('columns')) ? $this->_getOption('columns') : $model->info('cols');
    	if (is_array($columns)) {
			$i = 0;
			foreach ($columns as $field) {
				if ($field) {
					$columns[$i] = new DataTableColumn($field);
					if ($field == $this->_getModelPrimaryKey() AND !$this->_showKeyKeyColumn()) {
						$columns[$i]->setIsVisible(FALSE);
					}
				}
				$i++;
			}
        }
        
    	if ($this->_hasActionColumn()) {
            $col = new DataTableColumn($this->_getActionColumnHeader(), 
            						   null, 
            						   DataTableColumn::COLUMN_TYPE_ACTION, 
            						   $this->_getActionColumnHeader());

            $col->setClass($classColumnAction);
            
            $col->setLink("{$this->_getActionColumnUrl()}/{$this->_getModelPrimaryKey()}/");
            $col->setTarget($this->_getActionColumnTarget());
            $this->columns[] = $col;
	    }
        
        if ($this->_hasCheckboxColumn()) {
	            $this->addCheckColumn($atributoHtml);
	    }
	    
            
	    if ($this->_hasRadioColumn()) {
            $this->addRadioColumn($atributoHtml);
        }
        
        
		
	    
        $this->addManyColumns($columns);
    
       

        return $this;
    }
    
	protected function _hasCheckboxColumn()
	{
    	return $this->_getOption('displayCheckboxColumn') ? $this->_getOption('displayCheckboxColumn') : false; 
    }
    
	protected function _hasRadioColumn()
	{
    	return $this->_getOption('displayRadioColumn') ? $this->_getOption('displayRadioColumn') : false; 
    }
    
	protected function _hasActionColumn()
	{
    	return $this->_getOption('displayActionColumn') ? $this->_getOption('displayActionColumn') : false; 
    }
    
    protected function _showKeyKeyColumn()
    {
    	return $this->_getOption('displayKeyColumn') ? $this->_getOption('displayKeyColumn') : false; 
    }
    
    protected function _getActionColumnHeader()
    {
    	return $this->_getOption('actionColumnHeader') ? $this->_getOption('actionColumnHeader') : 'Editar'; 
    }
    
    protected function _getActionColumnUrl()
    {
    	return $this->_getOption('actionColumnUrl') ? 
    		$this->_getOption('actionColumnUrl') : $this->_getActionColumnDefaultUrl();
    }
    
	protected function _getActionColumnDefaultUrl()
    {        
        $controller = Zend_Controller_Front::getInstance()->getRequest()->getParam('controller');
        $url = $this->_getUrl(array('controller' => $controller, 'action' => 'editar'), null, true);
        
        $params = array();
        foreach (Zend_Controller_Front::getInstance()->getRequest()->getUserParams() as $name => $value) {
        	if (!in_array($name, $this->_getForbiddenUrlParameters())) {
                $params[$name] = $value;
            }
        }   

        foreach ($params as $name => $value) {
            $url .= "/$name/$value";
        }        
        return $url;
    }
    
	protected function _getActionColumnTarget()
    {
    	return $this->_getOption('actionColumnTarget') ? $this->_getOption('actionColumnTarget') : null; 
    }
    
    /**
     * Render
     */
    public function render($reset = false)
    {
        $this->loadData();
        $this->html  = $this->renderToolBar();
        $this->html .= '<div class="table">';
        $this->html .= '<table cellspacing="1">';
        $this->html .= '<thead>';
        $this->html .= $this->renderHeader();
        $this->html .= $this->renderFilter();
        $this->html .= '</thead>';        
        $this->html .= $this->renderFooter();
        $this->html .= $this->renderBody();
        $this->html .= '</table>';
        $this->html .= '</div>';
        if ($reset) {
            $this->columns = null;
        }
        return $this->html;
    }
    
    /**     
     * Render ToolBar
     */
    public function renderToolBar()
    {
        $html  = '';
        if ($this->_isSearchable()) {
            $html .= '<input name="btnAcao" type="submit" value="Pesquisar" class="bt_acao ui-corner-all" /> ';
        }        
        return $html;
    }
    
    protected function _isSearchable()
    {
    	return $this->_getOption('displaySearch') ? $this->_getOption('displaySearch') : false;
    }
    
    protected function _getOption($key)
    {
    	return array_key_exists($key, $this->_options) ? $this->_options[$key] : null;
    }
    
    /**
     * Render Header
     */
    public function renderHeader()
    {
        $html = "<tr>";
        foreach ($this->_getColumns() as $col) {
           $html .= $col->renderHeader();        
        }       
        $html .= "</tr>";         
        return $html;
    }
    
    /**
     * Render Filter
     */
    public function renderFilter()
    {
        $html = '';                    
        if ($this->_isSearchable()) {
            $html = "<tr>";
            foreach ($this->_getColumns() as $col) {
               $html .= $col->renderFilter($this->data);        
            }       
            $html .= "</tr>";
        }                 
        return $html;
    }
    
    /**
     * Render Body
     */
    public function renderBody()
    {
        $html = '<tbody>';
        if ($this->_getNumberOfRows() > 0) {
            for ($i = $this->_getFirstRowIndex(); $i < $this->_getLastRowIndex() ; $i++) {
                $odd = ($i % 2 != 0) ? 'class="odd"' : false; 
                $html .= "<tr $odd>";
                foreach ($this->_getColumns() as $col) {
                	$html .= $col->renderBody($this->data[$i], $this->_getModelPrimaryKey());
                }
                $html .='</tr>';    
            }        
        } else {
            $html .= "<tr><td colspan=\"{$this->_getNumberOfColumns()}\">{$this->_getEmptyMessage()}</td></tr>";
        }
        $html .= '</tbody>';
         
        return $html;
    }
    
    protected function _getEmptyMessage()
    {
    	return $this->_getOption('emptyMessage') ? 
    		$this->_getOption('emptyMessage') : 'A pesquisa não retornou nenhum registro.';
    }
    
    /**
     * Render Footer
     */
    private function renderFooter()
    {
        $html = '';
        if ($this->_hasFooter()) {
            $pagina = $this->_getCurrentPage();
            $totalItens = $this->_getNumberOfRows(); 
            $primeiroItem = $this->_getFirstRowIndex();
            $ultimoItem = $this->_getLastRowIndex();
                
            $html  = '<tfoot>';                
            $html .= '<tr>';
            $html .= "<td colspan='{$this->_getNumberOfColumns()}'>";
            $primeiroExibindo = ($this->_getNumberOfRows() > 0) ? $primeiroItem + 1 : 0;
            $html .= "Exibindo de $primeiroExibindo à $ultimoItem de $totalItens registros.";
            $html .= "</td>";
            $html .= "</tr>";
            
            if ($this->_hasPagination() AND ($this->_getNumberOfRows() > $this->_getPageSize())) {
            	$html .= "<tr class='pagination'>";
                $html .= "<td colspan='{$this->_getNumberOfColumns()}'>";
                                   
                // Primeira Página da Lista
                $primeiraPagina = ($pagina > 2) ? $pagina - 2 : 1;
                
                // Total de Páginas da Lista
                $totalPaginas = floor($totalItens / $this->_getPageSize());
                if ($totalItens % $this->_getPageSize() > 0) {
                    $totalPaginas++;  
                }
                
                // Última Página
                if ($primeiraPagina + 4 > $totalPaginas) {
                    $ultimaPagina = $totalPaginas;
                    $primeiraPaginaOriginal = $primeiraPagina;
                    for ($i = 1; $i < 5 - ($ultimaPagina - $primeiraPaginaOriginal); $i++) {
                        if ($primeiraPagina <= 1) {
                        	break;
                        }
                    	$primeiraPagina--;
                    }
                } else {
                    $ultimaPagina = $primeiraPagina + 4;   
                }
                    
                if ($pagina > 1) {
                	$html .= "<a class='first' href='{$this->_getPageUrl(1)}'>Primeira</a>";
                }
                
                if ($pagina > 1) {
                	$html .= "<a class='previous' href='{$this->_getPageUrl($pagina - 1)}'>«</a>";
                }                
                for ($i = $primeiraPagina; $i <= $ultimaPagina; $i++) {
                    $html .= ($i != $pagina) ? 
                    	"<a href='{$this->_getPageUrl($i)}'>$i</a>" : "<span class='current'>$i</span>";
                }
                if ($ultimoItem < $totalItens) {
                	$html .= "<a class='next' href='{$this->_getPageUrl($pagina + 1)}'>»</a>";
                }
                
                if ($ultimoItem < $totalItens) {
                	$html .= "<a class='last' href='{$this->_getPageUrl($totalPaginas)}'>Última</a>";
                }
                
                $html .= "</td>";
                $html .= "</tr>";         
            }
            $html .= "</tfoot>";
        }                   
        
        return $html;
    }
    
    protected function _hasPagination()
    {
    	return ($this->_getOption('displayPagination')) ? $this->_getOption('displayPagination') : true;
    }
    
    protected function _getPageSize()
    {
    	return $this->_getOption('pageSize') ? $this->_getOption('pageSize') : 10;
    }
    
    protected function _getPageUrl($page)
    {
    	$controller = Zend_Controller_Front::getInstance()->getRequest()->getParam('controller');
		$action = Zend_Controller_Front::getInstance()->getRequest()->getParam('action');
		
		return $url = $this->_getUrl(array('controller' => $controller, 'action' => $action, 'pagina' => $page));
    }
    
	/**
     * Retorna uma url valida
     * @param array $urlOptions
     * @param $name
     * @param $reset
     * @param $encode
     * @return String
     */
    protected function _getUrl(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
    {
        $router = Zend_Controller_Front::getInstance()->getRouter();
        return $router->assemble($urlOptions, $name, $reset, $encode);
    }
    
    protected function _hasFooter()
    {
    	return $this->_getOption('displayFooter') ? $this->_getOption('displayFooter') : true;
    }
        
    /**
     * Total de Colunas
     * @return int
     */
    public function _getNumberOfColumns()
    {
        return count($this->_getColumns());
    }
    
    /**
     * Total de Linhas
     * @return int
     */
    protected function _getNumberOfRows()
    {
        return count($this->data);
    }
    
    /**
     * firstItemIndex
     * @return int
     */
    protected function _getFirstRowIndex()
    {
        return ($this->_getCurrentPage() - 1) * $this->_getPageSize();
    }
    
    /**
     * lastItemIndex
     * @return int
     */
    public function _getLastRowIndex()
    {
        return ($this->_getCurrentPage() * ($this->_getPageSize()) > $this->_getNumberOfRows()) ? 
            $this->_getNumberOfRows() : ($this->_getCurrentPage() * $this->_getPageSize());
    }
    
    /**
     * setModel
     */
    public function setModel($value)
    {
        $this->_model = $value;
        return $this;
    }    
    
    /**
     * getModel
     * @return object
     */
    public function getModel()
    {
        return $this->_model;
    }        
    
    /**
     * Add Column
     * @param DataTableColumn
     */
    public function addColumn(DataTableColumn $column)
    {
        if ($this->_getNumberOfColumns() > 0) {
            $col = $this->columns[$this->_getNumberOfColumns()-1];
            if ($col->getType() == DataTableColumn::COLUMN_TYPE_ACTION) {
                $this->columns[] = $col;
                $this->columns[$this->_getNumberOfColumns()-2] = $column;
            } else {
                $this->columns[] = $column;
            }
        } else {
            $this->columns[] = $column;
        }        
        return $this;
    }
    
    /**
     * Add Many Column
     * @param array
     */
    public function addManyColumns($columns)
    {
        if ($columns) {
            foreach ($columns as $col) {
                $this->addColumn($col);
            }
        } 
        
        return $this;
    }

    /**
     * Add Radio Column
     * @return DataTableColumn
     */
    public function addRadioColumn($atributoHtml='')
    {
        $col = new DataTableColumn('', null, DataTableColumn::COLUMN_TYPE_RADIO);
        
        $col->setAtributoHtml($atributoHtml);
        $this->columns[] = $col;
        return $this;
    }    
    
    /**
     * Add Check Column
     * @return DataTableColumn
     */
    public function addCheckColumn($atributoHtml='')
    {
        $col = new DataTableColumn('', null, DataTableColumn::COLUMN_TYPE_CHECK);
        
        $col->setAtributoHtml($atributoHtml);
        $this->columns[] = $col;
    }    

    /**
     * get Column
     * @return array
     */
    protected function _getColumns()
    {
        return $this->columns;
    }
    
    protected function _getColumn($columnName)
    {
        return $this->columns[$columnName];
    }
    
    protected function _isSearchRequest()
    {
    	return Zend_Controller_Front::getInstance()->getRequest()->isPost() && 
               Zend_Controller_Front::getInstance()->getRequest()->getParam('btnAcao') == 'Pesquisar';
    }
    
    /**
     * Prepara o valor digitado (selecionado) no filtro de pesquisa
     * removendo mascaras, fazendo uppercase, etc
     * @param $campo string
     * @param $valor string|int
     * @return string|int
     */
    public function prepareValueFilter($campo,$valor)
    {
        return strtoupper($valor);
    }
    
    /**
     * Load Page
     */
    public function _getCurrentPage()
    {
        return Zend_Controller_Front::getInstance()->getRequest()->getParam('pagina', 1);
    }
    
    /**
     * Load Data
     */
    public function loadData()
    {       
        $metodo = $this->_getFetchMethod();
        $this->data = $this->_model->$metodo($this->_getCriteria(), $this->order);
    }
    
    protected function _getFetchMethod()
    {
    	return ($this->_getOption('fetchMethod')) ? $this->_getOption('fetchMethod') : 'fetchAll';
    }
    
    protected function _getCriteria()
    {
    	$criteria = $this->_getOption('criteria') ? $this->_getOption('criteria') : null;
    	return $this->_loadCriteria($criteria);
    }
    
	protected function _loadCriteria($criteria)
    {        
        $this->loadDataFilters();
        if (!isset($criteria)) {
            $criteria = $this->prepareFilterSQL($this->dataFilters, $this->_model->info('name'));
        } else {
            $sql = $this->prepareFilterSQL($this->dataFilters, $this->_model->info('name'));
            
            if (!empty($sql)) {
                $criteria .= ' AND '. $sql;
            }            
        }
             
        return $criteria;
    }
    
	/**
     * Load Data Filters
     * @return void
     */
    public function loadDataFilters()
    {        
        #Codigo de Pesquisa referente a ação do botao pesquisar
        $formPesquisa = array();
        if ($this->_isSearchable()) {    
            if ($this->_isSearchRequest()) {
                foreach (Zend_Controller_Front::getInstance()->getRequest()->getPost() as $key => $value) {
                    $pesquisa = array();
                    if (strpos($key, 'fil_') !== FALSE AND (!empty($value))) {
                        // Quando o nome do campo tem espaços a pesquisa (POST) altera os espaços por underline,  
                        // estamos voltando para espaços.
                        //$pesquisa['campo'] = str_replace('_', ' ', substr($key, 4, strlen($key)));
                        $pesquisa['campo'] = substr($key, 4, strlen($key));
                        $pesquisa['valor'] = $this->prepareValueFilter($pesquisa['campo'], $value);
                     
                        $formPesquisa[] = $pesquisa;
                    }        
                }
            }
        }
                
        $this->dataFilters = $formPesquisa;
    }
    
    protected function _getForbiddenUrlParameters()
    {
    	return array('controller', 'action', 'module', 'pagina', $this->_getModelPrimaryKey(), 'method');
    }
    
    protected function _getModelPrimaryKey()
    {
    	return ($this->_getOption('key')) ? $this->_getOption('key') : array_shift($this->_model->info('primary'));
    }
    
    public function addColumnsData($arrayColumnsName)
    {        
        foreach ($arrayColumnsName as $col) {
        	$name = (is_string($col)) ? $col : $col['name'];
        	$header = (is_string($col)) ? $col : (isset($col['header'])) ? $col['header'] : $col['name'];
            
            $dataType = (isset($this->_model->columnTypes) AND isset($this->_model->columnTypes[$name])) ? 
                $this->_model->columnTypes[$name] : NULL;
                
            $tableColumn = new DataTableColumn($name, DataTableColumn::COLUMN_TYPE_DATA, $header, $dataType);
            
            $this->addColumn($tableColumn);
        }
        return $this;
    }
    
    public function addFiltersCombo($arrayColumnsName)
    {
        if (is_array($arrayColumnsName)) {
            foreach ($arrayColumnsName as $col) {
                foreach ($this->columns as $dtcol) {
                    if ($dtcol->getName() == $col) {
                        $dtcol->setFilterType(DataTableColumn::FILTER_TYPE_COMBOBOX);
                        break;
                    }
                }            
            }
        }
        return $this;
    }
    
    public function prepareFilterSQL(array $filterData, $table)
    {
    	if (!$filterData) {
    		return null;
    	}
    	
        $sql = '';
        $flag = false;    
        foreach ($filterData as $data) {
            if (!empty($data['valor'])) {                          
                $sql .= ($flag)? ' AND ': '';     
                $sql .= $this->prepareOneFilterSQL($data['campo'], $data['valor'], $table);
              
                $flag = true;
            }              
        }        
        return $sql;     
    }
    
    public function prepareOneFilterSQL($campo, $valor, $table)
    {
        $sql = ' ';
        if (trim($valor) <> "" && $campo <> "0") {
            if (strpos($campo, " ") > -1) {
                $sql .= '"' . $campo. '" ';
            } else {
                $sql .= $campo . " ";
            }
            
            $tipo = $this->getFieldType($campo);

            // Campo numérico e um valor não numérico, retornar nada
            
            if ($tipo == 'numeric' && (!is_numeric($valor) OR ($tipo == 'date'))) {
            	$sql .= "IS NULL AND $campo IS NOT NULL ";    
            } else {
            	if ($tipo == 'string') {
            		$sql .= " ilike '%$valor%' ";
            	} elseif ($tipo == 'date') {
            		$sql .= " = '$valor' ";
            	} else {
            		$sql .= " = $valor ";
            	}
            }
        }
        
        return $sql;
    }
    
    public function getFieldType($field)
    {
        $fields = $this->_getModelFieldTypes();
        $tipo = $fields[$field];
        
        if ($tipo) {
            if (strpos($tipo, 'int') > -1) {
                return 'numeric';
            } elseif (strpos($tipo, 'varchar') > -1 OR strpos($tipo, 'text') > -1) {
                return 'string';
            } elseif (strpos($tipo, 'date') > -1) {
                return 'date';
            }
        }
    }
    
	protected function _getModelFieldTypes()
    {
        $metadata = $this->getModel()->info('metadata');
        $campos = $this->getModel()->info('cols');
        
        $array = array();
        foreach ($campos as $campo) {
            $array[$campo] = $metadata[$campo]['DATA_TYPE'];
        }

        return $array;
    }
    
    public function setOrder($order)
    {
        $this->order = $order;
        return $this;
    }
    
public function exportCsv(array $columns = null) {
        $this->loadData($this->_model, $this->_getCriteria());
        $csv = array();
        $row = array();
        
        if (is_null($columns)) {
            foreach ($this->data[0] as $key => $value) {
                $columns[] = $key;
            }
        }
        
        for ($i = 0; $i < count($this->data); $i++) {
            $dataItem = $this->data[$i];
            for ($j = 0; $j < count($columns); $j++) {
                $row[$j] = $dataItem->{$columns[$j]};
            }
            
            $csv[] = $row;
        }

        $text = ($columns) ? implode(';', $columns) : '';
        $text .= "\r\n";
        
        foreach ($csv as $csvrow) {
            $rowText = '';
            foreach ($csvrow as $csvcolumn) {
                $rowText .= ';' . $csvcolumn;
            }
            $rowText = substr($rowText, 1);
            $text .= $rowText . "\r\n";
        }

        return $text;
    }
}